跳到主要内容

遗忘的关键字

  • mutable是为了突破const函数的限制而设计的
  • mutable成员变量将永远处于可改变的状态
  • mutable在实际的项目开发中被严禁滥用
  • mutable的深入分析
    • mutable成员变量破坏了只读对象的内部状态
    • const成员函数保证只读对象的状态不变性
    • mutable成员变量的出现无法保证状态不变性

编程实验

  • 成员变量的访问统计

    统计对象中某个成员变量的访问次数

    #include <iostream>
    #include <string>

    using namespace std;

    class Test {
    int m_value;
    int * const m_pCount;
    /* mutable int m_count; */
    public:
    Test(int value = 0) : m_pCount(new int(0)) {
    m_value = value;
    /* m_count = 0; */
    }

    int getValue() const {
    /* m_count++; */
    *m_pCount = *m_pCount + 1;
    return m_value;
    }

    void setValue(int value) {
    /* m_count++; */
    *m_pCount = *m_pCount + 1;
    m_value = value;
    }

    int getCount() const {
    /* return m_count; */
    return *m_pCount;
    }

    ~Test() {
    delete m_pCount;
    }
    };

    int main(int argc, char *argv[]) {
    Test t;

    t.setValue(100);

    cout << "t.m_value = " << t.getValue() << endl;
    cout << "t.m_count = " << t.getCount() << endl;

    const Test ct(200);

    cout << "ct.m_value = " << ct.getValue() << endl;
    cout << "ct.m_count = << ct.getCount() << endl;

    return 0;
    }

面试题:new关键字创建出来的对象位于什么地方?

被忽略的事实

  • new/delete的本质是C++预定义的操作符

  • C++对这两个操作符做了严格的行为定义

    • new:
      1. 获取足够大的内存空间(默认为堆空间)
      2. 在获取的空间中调用构造函数创建对象
    • delete:
      1. 调用析构函数销毁对象
      2. 归还对象所占用的空间(默认为堆空间)
  • 在C++中能够重载new/delete操作符

    • 全局重载(不推荐)
    • 局部重载(针对具体类型 )

    重载new/delete的意义在于改变动态对象创建时的内存分配方式

  • new/delete的重载方式

    //static member function
    void* operator new(unsigned int size) {
    void* ret = NULL;
    // ret point to allocated memory
    return ret;
    }

    //static member function
    void operator delete(void *p) {
    //free the memory which is pointed by p
    }

编程实验

  • 静态存储区中创建动态对象

    #include <iostream>
    #include <string>

    using namespace std;

    class Test {
    static const unsigned int COUNT = 4;
    static char c_buffer[];
    static char c_map[];

    int m_value;
    public:
    void* operator new (unsigned int size) {
    void* ret = nullptr;
    for(int i=0; i<COUNT; i++) {
    if( !c_map[i] ) {
    c_map[i] = 1;
    ret = c_buffer + i * sizeof(Test);
    cout << "succeed to allocate memory: " << ret << endl;
    break;
    }
    }
    return ret;
    }

    void operator delete (void* p) {
    if( p != nullptr ) {
    char* mem = reinterpret_cast<char*>(p);
    int index = (mem - c_buffer) / sizeof(Test);
    int flag = (mem - c_buffer) % sizeof(Test);
    if( (flag == 0) && (0 <= index) && (index < COUNT) ) {
    c_map[index] = 0;
    cout << "succeed to free memory: " << p << endl;
    }
    }
    }
    };

    char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0};
    char Test::c_map[Test::COUNT] = {0};

    int main(int argc, char *argv[]) {
    cout << "===== Test Single Object =====" << endl;

    Test* pt = new Test;

    delete pt;

    cout << "===== Test Object Array =====" << endl;

    Test* pa[5] = {0};

    for(int i=0; i<5; i++) {
    pa[i] = new Test;

    cout << "pa[" << i << "] = " << pa[i] << endl;
    }

    for(int i=0; i<5; i++) {
    cout << "delete " << pa[i] << endl;

    delete pa[i];
    }

    return 0;
    }

面试题:如何在指定的地址上创建C++对象?

设计思路

  • 解决方案
    • 在类中重载new/delete操作符
    • 在new的操作符重载函数中返回指定的地址
    • 在delete操作符重载中标记对应的地址可用

编程实验

  • 自定义动态对象的存储空间

    #include <iostream>
    #include <string>
    #include <cstdlib>

    using namespace std;

    class Test {
    static unsigned int c_count;
    static char* c_buffer;
    static char* c_map;

    int m_value;
    public:
    static bool SetMemorySource(char* memory, unsigned int size) {
    bool ret = false;
    c_count = size / sizeof(Test);
    ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char)))));

    if( ret ) {
    c_buffer = memory;
    } else {
    free(c_map);
    c_map = nullptr;
    c_buffer = nullptr;
    c_count = 0;
    }
    return ret;
    }

    void* operator new (unsigned int size) {
    void* ret = nullptr;
    if( c_count > 0 ) {
    for(int i=0; i<c_count; i++) {
    if( !c_map[i] ) {
    c_map[i] = 1;
    ret = c_buffer + i * sizeof(Test);
    cout << "succeed to allocate memory: " << ret << endl;
    break;
    }
    }
    } else {
    ret = malloc(size);
    }
    return ret;
    }

    void operator delete (void* p) {
    if( p != nullptr ) {
    if( c_count > 0 ) {
    char* mem = reinterpret_cast<char*>(p);
    int index = (mem - c_buffer) / sizeof(Test);
    int flag = (mem - c_buffer) % sizeof(Test);

    if( (flag == 0) && (0 <= index) && (index < c_count) ) {
    c_map[index] = 0;

    cout << "succeed to free memory: " << p << endl;
    }
    } else {
    free(p);
    }
    }
    }
    };

    unsigned int Test::c_count = 0;
    char* Test::c_buffer = nullptr;
    char* Test::c_map = nullptr;

    int main(int argc, char *argv[]) {
    char buffer[12] = {0};

    Test::SetMemorySource(buffer, sizeof(buffer));

    cout << "===== Test Single Object =====" << endl;

    Test* pt = new Test;

    delete pt;

    cout << "===== Test Object Array =====" << endl;

    Test* pa[5] = {0};

    for(int i=0; i<5; i++) {
    pa[i] = new Test;

    cout << "pa[" << i << "] = " << pa[i] << endl;
    }

    for(int i=0; i<5; i++) {
    cout << "delete " << pa[i] << endl;

    delete pa[i];
    }

    return 0;
    }
  • new[]/delete[]与new/delete完全不同

    • 动态对象数组创建通过new[]完成
    • 动态对象数组的销毁通过delete[]完成
    • new[]/delete[]能够被重载,进而改变内存管理方式
  • new[]/delete[]的重载方式

    //static member function
    void* operator new[ ](unsigned int size) {
    return malloc(size);
    }

    //static member function
    void operator delete[ ](void *p) {
    free(p);
    }
  • 注意事项

    • new[ ]实际需要返回的内存空间可能比期望的要多
    • 对象数组占用的内存中需要保存数组信息
    • 数组信息用于确定构造函数和析构函数的调用次数

编程实验

  • 动态数组的内存管理
#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

class Test {
int m_value;
public:
Test() {
m_value = 0;
}

~Test() {
}

void* operator new (unsigned int size) {
cout << "operator new: " << size << endl;

return malloc(size);
}

void operator delete (void* p) {
cout << "operator delete: " << p << endl;

free(p);
}

void* operator new[] (unsigned int size) {
cout << "operator new[]: " << size << endl;

return malloc(size);
}

void operator delete[] (void* p) {
cout << "operator delete[]: " << p << endl;

free(p);
}
};

int main(int argc, char *argv[]) {
Test* pt = NULL;

pt = new Test;

delete pt;

pt = new Test[5];

delete[] pt;

return 0;
}

小结

  • new/delete的本质为操作符
  • 可以通过全局函数重载new/delete(不推荐)
  • 可以针对具体的类重载new/delete
  • new[]/delete[]与new/delete完全不同
  • new[]/delete[]也是可以被重载的操作符
  • new[]返回的内存空间可能要比期望的要多